#       Challenge.py
#       
#       Copyright 2008 Joshua Scotton <josh@joshuascotton.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

__licence__ = "GPLv2"
__copyright__ = "2008, Joshua Scotton"
__author__ = "Joshua Scotton <josh@joshuascotton.com>"

import os
from utils.utils import *

from xml.dom import minidom


CONFIG_DIR = os.path.expanduser('~/.config/challenge_helper/challenge.conf')

class Challenge:
    def __init__(self):
        """
        This sets up the Challenge object
        """
        #Source Filename
        self.file = ""
        #Challenge Name
        self.name = ""
        #Challenge Code, ie could be a number
        self.code = ""
        #Link to the entry thread for the challenge
        self.entry_thread_link = ""
        #Link to the voting thread for the challenge
        self.voting_thread_link = ""
        #Array of entries
        self.entries = []
        #Array of the Challenge types (categories)
        self.types = []
    
    def setFile(self, file):
        """
        This sets the input file
        @param file (String) The input file either fixed or relative to the current directory
        """
        self.file = file
    
    def toXML(self):
        """
        This returns an xml representation of the Challenge object
        @return (String) Formatted XML
        """
        #setup base xml
        xml_doc = minidom.getDOMImplementation().createDocument(None, "Challenge", None)
        root_node = xml_doc.documentElement
        
        #add challenge name
        element = xml_doc.createElement('Name')
        element.appendChild(xml_doc.createTextNode(self.name.strip()))
        root_node.appendChild(element)
        #add challenge code
        element = xml_doc.createElement('Code')
        element.appendChild(xml_doc.createTextNode(self.code.strip()))
        root_node.appendChild(element)
        #only add thread links if they have content in them
        if not self.entry_thread_link == "":
            element = xml_doc.createElement('EntryThreadLink')
            element.appendChild(xml_doc.createTextNode(self.entry_thread_link.strip()))
            root_node.appendChild(element)
        if not self.voting_thread_link == "":
            element = xml_doc.createElement('VotingThreadLink')
            element.appendChild(xml_doc.createTextNode(self.voting_thread_link.strip()))
            root_node.appendChild(element)
        
        #loop through entries and add their xml representation
        for entry in self.entries:
            root_node.appendChild(entry.toXML())
        
        #loop through types and add
        for type in self.types:
            element = xml_doc.createElement('Type')
            sub1 = xml_doc.createElement('TypeName')
            sub2 = xml_doc.createElement('TypeCat')
            sub1.appendChild(xml_doc.createTextNode(type[0]))
            sub2.appendChild(xml_doc.createTextNode(type[1]))
            element.appendChild(sub1)
            element.appendChild(sub2)
            root_node.appendChild(element)            
        
        #return the xml representation
        return xml_doc.toprettyxml()
        
    def load(self):
        """
        Populates data from preset xml source
        """
        #this calls the loadFromFile with the challenge's preset source file
        self.loadFromFile(self.file)
    
    
    def addEntryItem(self, artist, filename, type, source=""):
        """
        Quick add for entry item, won't create duplicate entries
        @param artist (String) This is the artist name
        @param filename (String) This is the link to the entryitem file
        @param type (int) This is the Challenge type index of the item type
        @param source (String) This is optional, defaults to "". It is a link to a source file for the entry
        """
        #we first check if artist already has an entry
        #if he does we just add another item to it otherwise we create a new entry
        the_entry = find(lambda entry: entry.artist == artist, self.entries)
        if not the_entry == None:
            the_entry.addItem(filename, type, source)
        else:
            entry = Entry()
            entry.challenge = self
            entry.artist = artist
            entry.addItem(filename, type, source)
            self.entries.append(entry)
        
        
    def addType(self, type, cat):
        """
        Adds a type (category) to the challenge type array
        @param type (String) name of the type
        """
        self.types.append((type, cat))
        
    def setup(self, name, code, entriesLink="", votingLink=""):
        """
        This setups the challenge with the critical information. It's not needed if you are loading from xml
        @param name (String) Challenge name
        @param code (String) Challenge code
        @param entriesLink (String) Link to entries thread, defaults to ""
        @param votingLink (String) Link to voting thread, defaults to ""
        """
        self.name = name
        self.code = code
        self.entry_thread_link = entriesLink
        self.voting_thread_link = votingLink
        
    def save(self):
        """
        Saves the xml representation of the challenge to the preset xml file.
        It will overwrite the existing file without a warning
        """
        if os.path.isfile(self.file):
            os.remove(self.file)
        file = open(self.file, 'w')
        file.write(self.toXML())
        file.close()
    
    def toString(self):
        """
        This will create a readable String representation of the challenge object
        @return (String) String representation
        """
        #TODO fill this in
        pass
        
    def loadFromFile(self, file=""):
        """
        This populates the Challenge object by reading xml from the file and sending it to loadFromXML. If the file parameter is not set or blank this will use the currently set xml file.
        This will throw an Exception if the file does not exist
        @param file (String) filename of source xml file, defaults to ""
        """
        if file == "":
            file = self.file
        if os.path.isfile(file):
            xml = minidom.parse(file)
            self.loadFromXML(xml)
        else:
            raise Exception("File Doesn't Exist")
        
    def loadFromXML(self, xml):
        """
        This populates the Challenge object from the passed xml
        @param xml (minidom xml document) XML data
        """
        #get nodes
        nodes = xml.documentElement.childNodes
        #loop through nodes and populate self
        for node in nodes:
            name = node.nodeName
            if name == "Name":
                self.name = node.childNodes[0].data.strip()
            elif name == "Code":
                self.code = node.childNodes[0].data.strip()
            elif name == "EntryThreadLink":
                self.entry_thread_link = node.childNodes[0].data.strip()
            elif name == "VotingThreadLink":
                self.voting_thread_link = node.childNodes[0].data.strip()
            elif name == "Type":
                tup1 = node.childNodes[1].childNodes[0].data.strip()
                tup2 = node.childNodes[3].childNodes[0].data.strip()
                self.addType(tup1, tup2)
            elif name == "Entry":
                entry = Entry()
                entry.load(node)
                self.addEntry(entry)
                
    def addEntry(self, new_entry):
        """
        This adds an Entry object to the entries array.
        It first checks if the entry is a duplicate and if so adds the items to the existing entry, otherwise it just adds the entry to the entries array
        @param new_entry (Entry) The entry to be added
        """
        the_entry = find(lambda entry: entry.artist == new_entry.artist, self.entries)
        if not the_entry == None:
            for item in new_entry.items:
                the_entry.addItem(item.filename, item.type, item.source)
        else:
            new_entry.challenge = self
            self.entries.append(new_entry)
            
    def getEntries(self):
        """
        Gets the entries array
        @return (array(Entry)) Array of Entry objects
        """
        return self.entries

class Entry:
    def __init__(self):
        """
        This setups the Entry object with default values
        """
        self.challenge = Challenge()
        self.artist = ""
        self.votes = 0
        self.complete = False
        self.winner = False
        self.items = []
        
    def toXML(self):
        """
        This creates a xml representation of the Entry object in a minidom xml node
        @return (minidom xml node) The XML representation
        """
        dom = minidom.parseString("<Entry/>")
        node = dom.documentElement
        element = dom.createElement("Artist")
        element.appendChild(dom.createTextNode(self.artist.strip()))
        node.appendChild(element)
        element = dom.createElement("Votes")
        element.appendChild(dom.createTextNode(str(self.votes).strip()))
        node.appendChild(element)
        element = dom.createElement("Complete")
        if self.complete:
            complete = "True"
        else:
            complete = "False"
        element.appendChild(dom.createTextNode(complete.strip()))
        node.appendChild(element)
        element = dom.createElement("Winner")
        if self.winner:
            winner = "True"
        else:
            winner = "False"
        element.appendChild(dom.createTextNode(winner.strip()))
        node.appendChild(element)
        for item in self.items:
            node.appendChild(item.toXML())
        return node
        
    def load(self, node):
        """
        This populates the Entry from a minidom xml node
        @param node (minidom xml node) The xml node to be used
        """
        nodes = node.childNodes
        for node in nodes:
            name = node.nodeName
            if name == "Artist":
                self.artist = node.childNodes[0].data.strip()
            elif name == "Votes":
                self.votes = int(node.childNodes[0].data.strip())
            elif name == "Complete":
                if node.childNodes[0].data.strip() == "True":
                    self.complete = True
                else:
                    self.complete = False
            elif name == "Winner":
                if node.childNodes[0].data.strip() == "True":
                    self.winner = True
                else:
                    self.winner = False
            elif name == "Item":
                item = Item()
                item.entry = self
                item.load(node)
                self.items.append(item)
                
        
    def addItem(self, filename, type, source=""):
        """
        This creates an Item object and adds it to the items array
        @param filename (String) The item filename
        @param type (int) The array index of the type
        @param source (String) The source filename, defaults to ""
        """
        item = Item()
        item.entry = self
        item.filename = filename
        item.type = type
        item.source = source
        self.items.append(item)
        
    def getItems(self):
        """
        Gets the items array
        @return (array(Item)) An array of Item objects
        """
        return self.items

class Item:
    def __init__(self):
        """
        Populates the Item with default values
        """
        self.entry = Entry()
        #filename for item
        self.filename = ""
        #This is the index of the item type in the type array in Challenge
        self.type = 0
        #Source document for item
        self.source = ""
    
    def toXML(self):
        """
        This creates a xml representation of the Item in a minidom node
        @return (minidom xml node) XML representation
        """
        dom = minidom.parseString("<Item/>")
        node = dom.documentElement
        element = dom.createElement("Filename")
        element.appendChild(dom.createTextNode(self.filename.strip()))
        node.appendChild(element)
        element = dom.createElement("Type")
        element.appendChild(dom.createTextNode(str(self.type).strip()))
        node.appendChild(element)
        if not self.source == "":
            element = dom.createElement("Source")
            element.appendChild(dom.createTextNode(self.source.strip()))
            node.appendChild(element)
        return node
    
    def load(self, node):
        """
        This populations the item from a minidom xml node
        @param node (minidom xml node) The xml to be used
        """
        nodes = node.childNodes
        for node in nodes:
            name = node.nodeName
            if name == "Filename":
                self.filename = node.childNodes[0].data.strip()
            elif name == "Type":
                self.type = int(node.childNodes[0].data.strip())
            elif name == "Source":
                self.source = node.childNodes[0].data.strip()
